package com.gitee.sop.layui.adminserver.module.background.admin.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gitee.sop.layui.adminserver.entrance.bo.ServiceContext;
import com.gitee.sop.layui.adminserver.module.background.admin.constact.SysMenuConstant;
import com.gitee.sop.layui.adminserver.module.background.admin.entity.SysApplication;
import com.gitee.sop.layui.adminserver.module.background.admin.entity.SysMenu;
import com.gitee.sop.layui.adminserver.module.background.admin.entity.SysMenuRole;
import com.gitee.sop.layui.adminserver.module.background.admin.entity.SysUser;
import com.gitee.sop.layui.adminserver.module.background.admin.mapper.SysMenuMapper;
import com.gitee.sop.layui.adminserver.module.background.admin.service.SysApplicationService;
import com.gitee.sop.layui.adminserver.module.background.admin.service.SysMenuRoleRelationService;
import com.gitee.sop.layui.adminserver.module.background.admin.service.SysMenuRoleService;
import com.gitee.sop.layui.adminserver.module.background.admin.service.SysMenuService;
import com.gitee.sop.layui.adminserver.module.background.admin.util.SysMenuUtil;
import com.gitee.sop.layui.adminserver.module.base.bo.ZTreeNode;
import com.gitee.sop.layui.adminserver.module.base.error.ServiceInterruptionException;
import com.gitee.sop.layui.adminserver.module.base.util.AssertUtil;
import com.gitee.sop.layui.adminserver.module.base.util.ObjectUtil;
import com.gitee.sop.layui.adminserver.module.base.util.SqlTreeUtil;
import com.gitee.sop.layui.adminserver.module.base.util.StringUtil;
import com.gitee.sop.layui.adminserver.module.base.vo.MenuTreeVo;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 菜单 记录系统所有的功能列表，并提供功能是否权限才能访问 服务实现类
 * </p>
 *
 * @author admin
 * @since 2019-11-14
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {

    @Lazy
    @Resource
    private SysMenuRoleRelationService sysRoleMenuRelationService;
    @Lazy
    @Resource
    private SysMenuRoleService sysMenuRoleService;
    @Lazy
    @Resource
    private SysApplicationService sysApplicationService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String add(int parentId, SysMenu sysMenu) {
        sysMenu.init().setCreatedTime(new Date()).setCreatorUuid(ServiceContext.me().checkedUserAndGet(SysUser.class).getUuid());
        if (ObjectUtil.isEmpty(sysMenu.getAppId())) {
            sysMenu.setAppId(ServiceContext.me().getCurrentApplication().getAppId());
        }

        if (parentId == 0) {
            sysMenu.initTree();
        } else {
            SysMenu parent = super.getById(parentId);
            AssertUtil.notNull(parent, ServiceInterruptionException.fail(400, "非法操作"));
            SqlTreeUtil.setInertInfo(parent, sysMenu);
            //插入节点需要更树信息，以保持当前树状态
            this.baseMapper.updateTreeInfoAtInsert(parent.getTreeGroup(), parent.getTreeLeftTraverseNumber());
        }
        save(sysMenu);

        return sysMenu.getUuid();
    }

    @Override
    public void deleteSelfAndChildrenById(int id) {
        //存在校验
        SysMenu sysMenu = this.baseMapper.selectById(id);
        AssertUtil.notNull(sysMenu, ServiceInterruptionException.fail(400, "非法操作"));
        //用户权限校验
        SysUser cur = ServiceContext.me().checkedUserAndGet(SysUser.class);
        AssertUtil.isTrue(cur.getUuid().equals(sysMenu.getCreatorUuid()), ServiceInterruptionException.fail(400, "非法操作"));

        //菜单所属应用校验 TODO 判断修改应用菜单用户是否有权修改
//        SysApplication curApplication = ServiceContext.me().getCurrentApplication();
//        AssertUtil.isTrue(curApplication.getAppId().equals(sysMenu.getAppId()) ,ServiceInterruptionException.fail(400 ,"非法操作"));

        //获取当前节点所有子级节点
        List<SysMenu> childList = this.baseMapper.selectChildListTreeBy(sysMenu.getTreeGroup(), sysMenu.getTreeLevel(), sysMenu.getTreeLeftTraverseNumber());
        List<String> uuidList = childList.stream().collect(ArrayList::new, (res, ele) -> res.add(ele.getUuid()), (l, r) -> {
        });
        uuidList.add(sysMenu.getUuid());

        //查询菜单角色是否有关联数据
        int count = sysRoleMenuRelationService.countInMenuUuids(uuidList);
        AssertUtil.isFalse(count > 0, ServiceInterruptionException.fail(400, StringUtil.concat("菜单或其子菜单有关联的角色，无法删除")));

        //删除自身和子菜单
        this.baseMapper.deleteByUuid(sysMenu.getAppId(), uuidList);

        //更新删除数据后的树结构
        this.baseMapper.updateTreeInfoAtDelete(sysMenu.getTreeGroup(), sysMenu.getTreeLeftTraverseNumber(), uuidList.size());
    }

    @Override
    public List<MenuTreeVo> searchVerticalMenuTree() {
        SysUser cur = ServiceContext.me().checkedUserAndGet(SysUser.class);
        SysApplication curApp = ServiceContext.me().getCurrentApplication();
        List<SysMenu> list = this.baseMapper.selectListBy(curApp.getAppId(), cur.getUuid(), 1, SysMenuConstant.SHOW_MODE_VERTICAL, SysMenuConstant.TYPE_MENU, null, null, null);
        return SysMenuUtil.switchMenuTree(list, 0);
    }

    @Override
    public List<MenuTreeVo> searchLevelMenuTree(int menuId) {
        SysMenu parentMenu = getById(menuId);
        AssertUtil.notNull(parentMenu, ServiceInterruptionException.fail(400, StringUtil.concat("不存在指定菜单", menuId)));

        SysUser cur = ServiceContext.me().checkedUserAndGet(SysUser.class);
        SysApplication curApp = ServiceContext.me().getCurrentApplication();

        List<SysMenu> list = this.baseMapper.selectListBy(curApp.getAppId(), cur.getUuid(), 1, SysMenuConstant.SHOW_MODE_LEVEL
                , SysMenuConstant.TYPE_MENU, parentMenu.getTreeGroup(), parentMenu.getTreeLevel(), parentMenu.getTreeLeftTraverseNumber());

        //添加父菜单
        list.add(0, parentMenu);

        return SysMenuUtil.switchMenuTree(list, menuId);
    }

    @Override
    public List<ZTreeNode> searchMenuTreeByRoleUuid(String appId, String roleUuid) {
        SysMenuRole sysMenuRole = sysMenuRoleService.getOneByUuid(roleUuid);
        AssertUtil.notNull(sysMenuRole, ServiceInterruptionException.fail(400, "角色不存在"));

        SysUser cur = ServiceContext.me().checkedUserAndGet(SysUser.class);
        SysApplication curApp = ServiceContext.me().getCurrentApplication();

        String realAppId = ObjectUtil.isEmpty(appId) ? curApp.getAppId() : appId;

        List<SysMenu> list;
        //获取可见菜单
        if (sysMenuRole.getTreeLevel() == 1) {
            list = baseMapper.selectAllByCreatorUuid(realAppId, cur.getUuid());
        } else {
            SysMenuRole parent = sysMenuRoleService.getOneBy(realAppId, sysMenuRole.getTreeGroup(), sysMenuRole.getTreeParentLeftTraverseNumber());
            list = baseMapper.selectListByRoleUuid(realAppId, parent.getUuid());
        }
        AssertUtil.notEmpty(list, ServiceInterruptionException.fail(400, "无可用菜单，请设置上级角色菜单权限"));
        //相同父菜单整合
        list = SysMenuUtil.mergeSameParent(list);

        List<String> menuUuidList = sysRoleMenuRelationService.getMenuUuidByRoleUuid(roleUuid);

        List<Map<String, Object>> sysMenuTreeList = SqlTreeUtil.tree(list);

        List<ZTreeNode> zTreeNodeList = new ArrayList<>(list.size());
        sysMenuTreeList.forEach(map -> {
            ZTreeNode zTreeNode = new ZTreeNode();
            zTreeNode.setName((String) map.get("name")).setId((Integer) map.get("id")).setPId((Integer) map.get("pid"));
            if (menuUuidList.contains(map.get("uuid"))) {
                zTreeNode.setChecked(true);
            }
            zTreeNodeList.add(zTreeNode);
        });

        zTreeNodeList.add(ZTreeNode.createParent());
        return zTreeNodeList;
    }

    @Override
    public List<Map<String, Object>> getListAndOrderPidSortByAppId(String appId) {
        String cur = ServiceContext.me().checkedUserAndGet(SysUser.class).getUuid();
        String realAppId = ObjectUtil.isEmpty(appId) ? ServiceContext.me().getCurrentApplication().getAppId() : appId;
        return SqlTreeUtil.tree(baseMapper.selectAllAndOrderByPidSortByCreatorUuidAndAppId(cur, realAppId));
    }

    @Override
    public SysMenu getOneMaxSortMenuByAppIdAndPid(String appId, int pid) {
        SysApplication sysApplication = ServiceContext.me().getCurrentApplication();
        String realAppId = ObjectUtil.isEmpty(appId) ? sysApplication.getAppId() : appId;
        if (pid == 0) {
            return this.baseMapper.selectChildMaxSortBy(realAppId, null, 0);
        }
        SysMenu sysMenu = super.getById(pid);
        AssertUtil.notNull(sysMenu, ServiceInterruptionException.fail(400, "非法操作"));
        return this.baseMapper.selectChildMaxSortBy(realAppId, sysMenu.getTreeGroup(), sysMenu.getTreeLeftTraverseNumber());
    }

    @Override
    public List<SysMenu> getListByUuid(List<String> uuidList) {
        SysApplication sysApplication = ServiceContext.me().getCurrentApplication();
        return this.baseMapper.selectListByUuid(sysApplication.getAppId(), uuidList);
    }

    @Override
    public List<SysMenu> getListByAppIdAndIds(String appId, List<Integer> menuIdList) {
        String realAppId = ObjectUtil.isEmpty(appId) ? ServiceContext.me().getCurrentApplication().getAppId() : appId;
        return this.baseMapper.selectListByIds(realAppId, menuIdList);
    }

    @Override
    public List<SysMenu> getTopListByAppIdAndCreatorUuidAndNames(String appId, String creatorUuid, List<String> menuNameList) {
        return this.baseMapper.selectTopListByAppIdAndCreatorUuidAndNames(appId, creatorUuid, menuNameList);
    }

    @Override
    public List<ZTreeNode> getMenuTreeForUseTemplate() {
        List<Map<String, Object>> sysMenuTreeList = getListAndOrderPidSortByAppId(ServiceContext.me().getCurrentApplication().getAppId());

        List<ZTreeNode> zTreeNodeList = new ArrayList<>(sysMenuTreeList.size());
        sysMenuTreeList.forEach(map -> {
            ZTreeNode zTreeNode = new ZTreeNode();
            zTreeNode.setName((String) map.get("name")).setId((Integer) map.get("id")).setPId((Integer) map.get("pid"));
            zTreeNodeList.add(zTreeNode);
        });

        zTreeNodeList.add(ZTreeNode.createParent());
        return zTreeNodeList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void setMenuByUseMenuTemplate(String appId, List<Integer> menuIdList) {

        if (!menuIdList.isEmpty()) {
            SysUser cur = ServiceContext.me().checkedUserAndGet(SysUser.class);
            //检索权限
            SysApplication sysApplication = this.sysApplicationService.getOneByAppIdAndCreatorUuid(appId, cur.getUuid());
            AssertUtil.notNull(sysApplication, ServiceInterruptionException.fail(400, "非法操作"));

            //TODO 检索添加菜单是否为当前用户可见菜单，目的：防止被恶意攻击刷取用户权限


            List<SysMenu> templateMenuList = this.getListByAppIdAndIds(ServiceContext.me().getCurrentApplication().getAppId(), menuIdList);
            AssertUtil.notEmpty(templateMenuList, ServiceInterruptionException.fail(400, "非法操作"));

            //通过名称查询菜单，过滤掉重复名称的菜单 TODO 只获取第一层级的菜单
            List<String> templateMenuNameList = templateMenuList.stream().collect(ArrayList::new, (res, ele) -> res.add(ele.getName()), (l, r) -> {
            });
            List<SysMenu> curAppNameMenuList = this.getTopListByAppIdAndCreatorUuidAndNames(appId, cur.getUuid(), templateMenuNameList);

            AssertUtil.isTrue(curAppNameMenuList.isEmpty(), ServiceInterruptionException.fail(400, "存在相同名称的菜单，无法批量添加"));

            //头部节点
            List<SysMenu> topNodeList = templateMenuList.stream().collect(ArrayList::new, (res, ele) -> {
                if (ele.getTreeParentLeftTraverseNumber() == 0) {
                    res.add(ele);
                }
            }, (l, r) -> {
            });
            AssertUtil.notEmpty(topNodeList, ServiceInterruptionException.fail(400, "非法操作"));

            List<SysMenu> needAddList = new ArrayList<>();
            topNodeList.forEach(sysMenu -> {
                List<SysMenu> childList = SqlTreeUtil.getChildList(templateMenuList, sysMenu);
                sysMenu.setId(null).setCreatedTime(new Date()).setCreatorUuid(cur.getUuid()).setAppId(appId).init().initTree();
                childList.forEach(child -> child.setId(null).setCreatedTime(new Date()).setCreatorUuid(cur.getUuid()).setAppId(appId).init().setTreeGroup(sysMenu.getTreeGroup()));
                needAddList.add(sysMenu);
                needAddList.addAll(childList);
            });

            super.saveBatch(needAddList);
        }
    }

    @Override
    public String append(int parentMenuId, String name, String uri, String description, String icon) {
        // TODO 组装sysMenu对象，并交给add保存
        SysMenu sysMenu = new SysMenu();
        sysMenu.setName(name).setUrl(uri).setDescription(description).setIcon(icon);
        return add(parentMenuId, sysMenu);
    }

    @Override
    public void deleteSelfAndChildrenByUuid(String menuUuid) {
        SysMenu old = this.baseMapper.selectByUuid(menuUuid);
        if (old != null) {
            deleteSelfAndChildrenById(old.getId());
        }
    }

    @Override
    public void deleteInUuid(String appId, List<String> menuUuidList) {
        this.baseMapper.deleteByUuid(appId, menuUuidList);
    }

    @Override
    public SysMenu getOneByUuid(String menuUuid) {
        return this.baseMapper.selectByUuid(menuUuid);
    }

    @Override
    public List<SysMenu> getListByAppId(String appId) {
        return this.baseMapper.selectByAppId(appId);
    }

}
